package com.mall.gateway.filter;

import com.alibaba.fastjson.JSON;
import com.mall.common.entity.gateway.GatewayLog;
import com.mall.common.util.IdGenerator;
import com.mall.gateway.service.GatewayLogService;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import lombok.extern.slf4j.Slf4j;
import org.reactivestreams.Publisher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

/**
 * 网关日志过滤器
 */
@Slf4j
@Component
public class GatewayLogFilter implements GlobalFilter, Ordered {
    
    @Autowired
    private GatewayLogService gatewayLogService;
    
    @Value("${jwt.secret:mall-secret}")
    private String jwtSecret;
    
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        LocalDateTime startTime = LocalDateTime.now();
        
        // 构建请求参数
        Map<String, Object> params = new HashMap<>();
        params.put("query", request.getQueryParams());
        params.put("path", request.getPath().value());
        
        // 创建网关日志对象
        GatewayLog gatewayLog = new GatewayLog();
        gatewayLog.setId(IdGenerator.nextId());
        gatewayLog.setTraceId(getTraceId(exchange));
        gatewayLog.setUserId(getUserId(request));
        gatewayLog.setIp(getClientIp(request));
        gatewayLog.setUri(request.getURI().toString());
        gatewayLog.setMethod(request.getMethodValue());
        gatewayLog.setParams(JSON.toJSONString(params));
        gatewayLog.setRequestTime(startTime);

        // 包装响应以捕获响应数据
        ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(exchange.getResponse()) {
            @Override
            public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
                if (body instanceof Flux) {
                    Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body;
                    return super.writeWith(fluxBody.buffer().map(dataBuffers -> {
                        DataBufferFactory bufferFactory = exchange.getResponse().bufferFactory();
                        DataBuffer join = bufferFactory.join(dataBuffers);
                        byte[] content = new byte[join.readableByteCount()];
                        join.read(content);
                        DataBufferUtils.release(join);
                        
                        // 记录响应数据
                        String responseData = new String(content);
                        gatewayLog.setResponseData(responseData);
                        
                        return bufferFactory.wrap(content);
                    }));
                }
                // 如果不是Flux类型，直接返回
                return super.writeWith(body);
            }
            
            @Override
            public Mono<Void> writeAndFlushWith(Publisher<? extends Publisher<? extends DataBuffer>> body) {
                return super.writeAndFlushWith(body);
            }
        };
        
        // 继续执行请求
        return chain.filter(exchange.mutate().response(decoratedResponse).build()).then(
            Mono.fromRunnable(() -> {
                ServerHttpResponse response = exchange.getResponse();
                LocalDateTime endTime = LocalDateTime.now();
                
                // 补充响应信息
                gatewayLog.setResponseTime(endTime);
                gatewayLog.setTime(java.time.Duration.between(startTime, endTime).toMillis());
                gatewayLog.setHttpStatus(response.getStatusCode() != null ? response.getStatusCode().value() : 0);
                
                // 异步保存日志，避免阻塞响应式线程
                gatewayLogService.saveLog(gatewayLog);
            })
        );
    }
    
    /**
     * 获取链路追踪ID
     */
    private String getTraceId(ServerWebExchange exchange) {
        return exchange.getRequest().getHeaders().getFirst("traceId");
    }
    
    /**
     * 获取用户ID
     */
    private Long getUserId(ServerHttpRequest request) {
        // 从token中解析用户ID
        String authorization = request.getHeaders().getFirst("Authorization");
        if (authorization != null && authorization.startsWith("Bearer ")) {
            try {
                // 提取Token
                String token = authorization.substring(7);
                // 解析Token获取用户信息
                Claims claims = Jwts.parser()
                        .setSigningKey(jwtSecret)
                        .parseClaimsJws(token)
                        .getBody();
                
                // 获取用户ID
                Object userIdObj = claims.get("userid");
                if (userIdObj != null) {
                    if (userIdObj instanceof Long) {
                        return (Long) userIdObj;
                    } else if (userIdObj instanceof Integer) {
                        return ((Integer) userIdObj).longValue();
                    } else {
                        return Long.valueOf(userIdObj.toString());
                    }
                }
            } catch (Exception e) {
                log.error("解析JWT Token获取用户ID失败: ", e);
            }
        }
        return null;
    }
    
    /**
     * 获取客户端IP
     */
    private String getClientIp(ServerHttpRequest request) {
        String ip = request.getHeaders().getFirst("X-Forwarded-For");
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeaders().getFirst("X-Real-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddress() != null ? request.getRemoteAddress().getAddress().getHostAddress() : "unknown";
        }
        return ip;
    }
    
    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE + 1; // 确保在认证过滤器之后执行
    }
}